/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*********************************************************************************************
    *   Filename        : power_wakeup.c

    *   Description     :

    *   Author          : Bingquan

    *   Email           : bingquan_cai@zh-jieli.com

    *   Last modifiled  : 2018-11-01 17:45

    *   Copyright:(c)JIELI  2011-2017  @ , All Rights Reserved.
*********************************************************************************************/
#include "power_wakeup.h"
#include <stdlib.h>
#include "asm/cpu.h"
#include "asm/power/p33.h"
#include "power_hw.h"
#include "asm/power_interface.h"
#include "asm/hwi.h"
#include "asm/rtc.h"
#include "gpio.h"
#include "asm/power/power_port.h"

#define LOG_TAG_CONST       WKUP
#define LOG_TAG             "[WKUP]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"

#define IRQ_FUNCTION_PROTOTYPE(idx, hdl) \
    __attribute__((interrupt("")))

#define IRQ_REGISTER(idx, hdl, prio) \
    request_irq(idx, prio, hdl, 0)

struct wakeup_driver_hdl hdl;

#define __this      (&hdl)

static u8 __hw_power_wakeup_handler(void);

const struct wakeup_param *power_wakeup_param;
static u8 wkup_src = 0;
static u8 wkup_pnd = 0;
static u8 wkup_en = 0xff;
static void (*charge_wakeup_callback)(void);
static void (*sub_wakeup_callback)(void);
static void (*chargestore_ldo5v_fall_callback)(void);
static void (*port_edge_wakeup_callback)(u8 index, u32 gpio);

#define P33_WKUP_DEFINE(type)           \
                                        \
void P33_SET_WKUP_##type(u8 data)       \
{                                       \
    p33_tx_1byte(P3_WKUP_##type, BIT(data));   \
}                                       \
                                        \
u8 P33_GET_WKUP_##type(void)            \
{                                       \
    return p33_rx_1byte(P3_WKUP_##type);  \
}                                       \
                                        \
void P33_AND_WKUP_##type(u8 data)       \
{                                       \
    p33_and_1byte(P3_WKUP_##type, ~BIT(data));  \
}                                       \
                                        \
void P33_OR_WKUP_##type(u8 data)        \
{                                       \
    p33_or_1byte(P3_WKUP_##type, BIT(data));   \
}                                       \

void port_edge_wakeup_control(u16 data)
{
    p33_tx_1byte(P3_WKUP_EN, data & 0xff);
}

void P33_DUMP_WKUP(void)
{
    g_printf("P33_WKUP_EN %x", p33_rx_1byte(P3_WKUP_EN));
    g_printf("P33_WKUP_EDGE %x", p33_rx_1byte(P3_WKUP_EDGE));
    g_printf("P33_WKUP_CPND %x", p33_rx_1byte(P3_WKUP_CPND));
    g_printf("P33_WKUP_PND %x", p33_rx_1byte(P3_WKUP_PND));
    g_printf("P33_WKUP_FLEN%x", p33_rx_1byte(P3_WKUP_FLEN));
    g_printf("P3_PORT_FLT %x", p33_rx_1byte(P3_PORT_FLT));
}

void RTC_DUMP_WKUP(void)
{
    r_printf("RTC_WKUP_EN 0x%x\n", p33_rx_1byte(R3_WKUP_EN));
    r_printf("RTC_WKUP_EDGE 0x%x\n", p33_rx_1byte(R3_WKUP_EDGE));
    r_printf("RTC_WKUP_CPND 0x%x\n", p33_rx_1byte(R3_WKUP_CPND));
    r_printf("RTC_WKUP_PND 0x%x\n", p33_rx_1byte(R3_WKUP_PND));
    r_printf("RTC_WKUP_FLEN 0x%x\n", p33_rx_1byte(R3_WKUP_FLEN));
    r_printf("RTC_PORT_FLT 0x%x\n", p33_rx_1byte(R3_PORT_FLT));
    r_printf("RTC_PORT_PU 0x%x\n", p33_rx_1byte(R3_PR_PU));
    r_printf("RTC_PORT_PD 0x%x\n", p33_rx_1byte(R3_PR_PD));
    r_printf("RTC_PORT_DIR 0x%x\n", p33_rx_1byte(R3_PR_DIR));
    r_printf("RTC_PORT_DIE 0x%x\n", p33_rx_1byte(R3_PR_DIE));
    r_printf("RTC_PORT_OUT 0x%x\n", p33_rx_1byte(R3_PR_OUT));
    r_printf("R3_OSL_CON 0x%x\n", p33_rx_1byte(R3_OSL_CON));
}

P33_WKUP_DEFINE(EN)
P33_WKUP_DEFINE(EDGE)
P33_WKUP_DEFINE(CPND)
P33_WKUP_DEFINE(PND)
P33_WKUP_DEFINE(FLEN)

#define WAKEUP_HOLD_MAX        100
static u8 wakeup_hold = 0;
static u8 __wakeup_is_idle(void)
{
    if (wakeup_hold) {
        wakeup_hold --;
    }
    return !wakeup_hold;
}

REGISTER_LP_TARGET(__wakeup_is_idle_target) = {
    .name = "wkup_hold",
    .is_idle = __wakeup_is_idle,
};

__attribute__((weak)) bool power_wakeup_is_hold(void)
{
    return TRUE;
}

static u32 __hw_wakeup_source(void)
{
    u8 sfr;
    u8 reason = 0;

    sfr = p33_rx_1byte(P3_WKUP_SRC);

    // External wakeup
    if (sfr & BIT(P3_WKUP_SRC_PCNT_OVF)) {
        log_info("P3_WKUP_SRC_PCNT_OVF");
    }
    if (sfr & BIT(P3_WKUP_SRC_EDGE)) {
        log_info("P3_WKUP_SRC_EDGE");
        reason |= __hw_power_wakeup_handler();
        if (power_wakeup_is_hold()) {
            wakeup_hold = WAKEUP_HOLD_MAX;
        }
    }
    if (sfr & BIT(P3_WKUP_SRC_SUB)) {
        log_info("P3_WKUP_SRC_SUB");
        if (IS_LVCMP_WKUP()) {
            LVCMP_PND_CLR();
            log_info("\nLVCMP\n");
        }
        if (IS_LDO5V_WKUP()) {
            LDO5V_PND_CLR();
            log_info("\nLDO5V\n");
            if (chargestore_ldo5v_fall_callback) {
                chargestore_ldo5v_fall_callback();
            }
        }
        if (IS_L5DEM_WKUP()) {
            L5DEM_PND_CLR();
            log_info("\nL5DEM\n");
        }
        if (sub_wakeup_callback) {
            sub_wakeup_callback();
        }
        reason |= __this->sub.attribute;
    }
    if (sfr & BIT(P3_WKUP_SRC_VDD50_LVD)) {
        log_info("P3_WKUP_SRC_VDD50_LVD");
        extern void __lvd_irq_handler(void);
        __lvd_irq_handler();
    }
    if (sfr & BIT(P3_WKUP_SRC_CHG)) {
        log_info("P3_WKUP_SRC_CHG");
        CHARGE_WKUP_PND_CLR();
        CHARGE_EDGE_DETECT_EN(0);
        CHARGE_LEVEL_DETECT_EN(0);
        if (charge_wakeup_callback) {
            charge_wakeup_callback();
        }
        reason |= __this->charge.attribute;
    }
    if (sfr & BIT(P3_WKUP_SRC_WDT_INT)) {
        log_info("P3_WKUP_SRC_WDT_INT");
        void wdt_isr(void);
        wdt_isr();
    }

    return reason;
}

static void P3_PORT_SET(u8 num, u8 group, u8 index)
{
    u8 x, y;
    switch (num) {
        case 0:
            x = P3_PORT_SEL10;
            break;
        case 1:
            x = P3_PORT_SEL11;
            break;
        case 2:
            x = P3_PORT_SEL12;
            break;
        case 3:
            x = P3_PORT_SEL13;
            break;
        case 4:
            x = P3_PORT_SEL14;
            break;
        case 5:
            x = P3_PORT_SEL15;
            break;
        case 6:
            x = P3_PORT_SEL16;
            break;
        case 7:
            x = P3_PORT_SEL17;
            break;
    }

    switch (group) {
        case GPIOA:
            y = 0;
            break;
        case GPIOB:
            y = BIT(4);
            break;
        case GPIOC:
            y = BIT(5);
            break;
        case GPIOD:
            y = BIT(4) | BIT(5);
        case GPIOUSB:   // USB DP/DM
            y = BIT(4) | BIT(5);
            break;
        case GPIOP33:
            y = BIT(4) | BIT(5);
            break;
    }

    P33_CON_SET(x, 0, 6, y | index);
}

static void P33_PORT_DUMP(void)
{
    log_info("P3_PORT_SEL10 %x",    p33_rx_1byte(P3_PORT_SEL10));
    log_info("P3_PORT_SEL11 %x",    p33_rx_1byte(P3_PORT_SEL11));
    log_info("P3_PORT_SEL12 %x",    p33_rx_1byte(P3_PORT_SEL12));
    log_info("P3_PORT_SEL13 %x",    p33_rx_1byte(P3_PORT_SEL13));
    log_info("P3_PORT_SEL14 %x",    p33_rx_1byte(P3_PORT_SEL14));
    log_info("P3_PORT_SEL15 %x",    p33_rx_1byte(P3_PORT_SEL15));
    log_info("P3_PORT_SEL16 %x",    p33_rx_1byte(P3_PORT_SEL16));
    log_info("P3_PORT_SEL17 %x",    p33_rx_1byte(P3_PORT_SEL17));
}

static const u8 PA_valid[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
static const u8 PB_valid[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
static const u8 PC_valid[] = {0, 1, 2, 3, 4, 5, 6, 7};
static const u8 PD_valid[] = {0, 1, 2, 3, 4};

static void __hw_general_port_wakeup_set(u8 index, struct port_wakeup *port)
{
    JL_PORT_FLASH_TypeDef *gpio = NULL;
    u8 group = (port->iomap / IO_GROUP_NUM) * IO_GROUP_NUM;     // Port Index
    u8 i = port->iomap % IO_GROUP_NUM;     // Port Index

    switch (group) {
        case GPIOA: {
                u8 j;
                for (j = 0; j < ARRAY_SIZE(PA_valid); j++) {
                    if (i == PA_valid[j]) {
                        log_info("PA_valid : %d", PA_valid[j]);
                        break;
                    }
                }
                ASSERT(j < ARRAY_SIZE(PA_valid), "Invalid I/O : %c - %d", 'A', i);
            }
            gpio = JL_PORTA;
            break;
        case GPIOB: {
                u8 j;
                for (j = 0; j < ARRAY_SIZE(PB_valid); j++) {
                    if (i == PB_valid[j]) {
                        log_info("PB_valid : %d", PB_valid[j]);
                        break;
                    }
                }
                ASSERT(j < ARRAY_SIZE(PB_valid), "Invalid I/O : %c - %d", 'B', i);
            }
            gpio = JL_PORTB;
            break;
        case GPIOD: {
                u8 j;
                for (j = 0; j < ARRAY_SIZE(PD_valid); j++) {
                    if (i == PD_valid[j]) {
                        log_info("PD_valid : %d", PD_valid[j]);
                        break;
                    }
                }
                ASSERT(j < ARRAY_SIZE(PD_valid), "Invalid I/O : %c - %d", 'D', i);
            }
            gpio = JL_PORTD;
            break;
        case GPIOC: {
                u8 j;
                for (j = 0; j < ARRAY_SIZE(PC_valid); j++) {
                    if (i == PC_valid[j]) {
                        log_info("PC_valid : %d", PC_valid[j]);
                        break;
                    }
                }
                ASSERT(j < ARRAY_SIZE(PC_valid), "Invalid I/O : %c - %d", 'D', i);
            }
            gpio = JL_PORTC;
            break;
    }

    if (gpio == NULL) {
        return;
    }

    gpio->DIR |= BIT(i);
    gpio->DIEH |= BIT(i);
    if (port->edge) {
        if (port->pullup_down_enable) {
            gpio->PU |= BIT(i);
            gpio->PD &= ~BIT(i);
        }
        P33_OR_WKUP_EDGE(index);
    } else {
        if (port->pullup_down_enable) {
            gpio->PU &= ~BIT(i);
            gpio->PD |= BIT(i);
        }
        P33_AND_WKUP_EDGE(index);
    }

    P33_OR_WKUP_CPND(index);
    P33_OR_WKUP_EN(index);
    if (port->filter_enable) {
        P33_OR_WKUP_FLEN(index);
    } else {
        P33_AND_WKUP_FLEN(index);
    }

    P3_PORT_SET(index, group, i);

    P33_CON_SET(P3_PORT_FLT, 0, 3, power_wakeup_param->filter);
}

#define USB_CON0        (JL_USB->CON0)
#define USB_IO_CON0     (JL_USB_IO->CON0)

#define USB_DP_OUT(x)   (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(0)) | ((x & 0x01) << 0))  )
#define USB_DM_OUT(x)   (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(1)) | ((x & 0x01) << 1))  )
#define USB_DP_DIR(x)   (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(2)) | ((x & 0x01) << 2))  )
#define USB_DM_DIR(x)   (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(3)) | ((x & 0x01) << 3))  )
#define USB_DP_PD(x)    (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(4)) | ((x & 0x01) << 4))  )
#define USB_DM_PD(x)    (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(5)) | ((x & 0x01) << 5))  )
#define USB_DP_PU(x)    (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(6)) | ((x & 0x01) << 6))  )
#define USB_DM_PU(x)    (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(7)) | ((x & 0x01) << 7))  )
#define USB_DP_DIE(x)   (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(9)) | ((x & 0x01) << 9))  )
#define USB_DM_DIE(x)   (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(10)) | ((x & 0x01) << 10)))
#define USB_DP_DIEH(x)  (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(13)) | ((x & 0x01) << 13)))
#define USB_DM_DIEH(x)  (USB_IO_CON0 = ((USB_IO_CON0 & ~BIT(14)) | ((x & 0x01) << 14)))

static void __hw_USB_port_wakeup_set(u8 index, struct port_wakeup *port)
{
    u8 mux_in;
    switch (port->iomap) {
        case IO_PORT_DP: {
                log_info("USB_valid : DP");
                mux_in = 8;
                USB_DP_DIEH(1);
                USB_DP_DIR(1);
                if (port->edge) {
                    USB_DP_PU(1);
                    USB_DP_PD(0);
                    P33_OR_WKUP_EDGE(index);
                } else {
                    USB_DP_PU(0);
                    USB_DP_PD(1);
                    P33_AND_WKUP_EDGE(index);
                }
            }
            break;
        case IO_PORT_DM: {
                log_info("USB_valid : DM");
                mux_in = 9;
                USB_DM_DIEH(1);
                USB_DM_DIR(1);
                if (port->edge) {
                    USB_DM_PU(1);
                    USB_DM_PD(0);
                    P33_OR_WKUP_EDGE(index);
                } else {
                    USB_DM_PU(0);
                    USB_DM_PD(1);
                    P33_AND_WKUP_EDGE(index);
                }
            }
            break;
        default:
            ASSERT(0, "Invalid I/O : %s - %d", "USB", port->iomap - GPIOUSB);
            break;
    }
    P33_OR_WKUP_CPND(index);
    P33_OR_WKUP_EN(index);

    P3_PORT_SET(index, GPIOUSB, mux_in);
}

static void __hw_P33_port_wakeup_set(u8 index, struct port_wakeup *port)
{
    u8 mux_in;
    switch (port->iomap) {
        case IO_LDOIN_DET: {
                log_info("P33_valid : LDOIN_DET");
                mux_in = 10;
                if (port->edge) {
                    P33_OR_WKUP_EDGE(index);
                } else {
                    P33_AND_WKUP_EDGE(index);
                }
            }
            break;
        case IO_LVCMP_DET: {
                log_info("P33_valid : LVCMP_DET");
                mux_in = 11;
                if (port->edge) {
                    P33_OR_WKUP_EDGE(index);
                } else {
                    P33_AND_WKUP_EDGE(index);
                }
            }
            break;
        default:
            ASSERT(0, "Invalid I/O : %s - %d", "P33", port->iomap - GPIOP33);
            break;
    }
    P33_OR_WKUP_CPND(index);
    P33_OR_WKUP_EN(index);

    P3_PORT_SET(index, GPIOP33, mux_in);
}

static void __hw_rtc_port_wakeup_set(u8 index, struct port_wakeup *rtc_port)
{
    if ((rtc_port->iomap < IO_PORT_PR_00) && (rtc_port->iomap > IO_PORT_PR_01)) {
        ASSERT(0, "rtc pr io over size \n");
    }

    p33_tx_1byte(R3_OSL_CON, 0);
    u8 dat = p33_rx_1byte(P3_VLD_KEEP);
    dat |= BIT(7); // doscen 晶振两脚模式,只用于32k晶振
    p33_tx_1byte(P3_VLD_KEEP, dat);
    u8 port    = rtc_port->iomap - IO_PORT_PR_00;
    rtc_port_pr_in(port);
    rtc_port_pr_die(port, 1);
    if (rtc_port->edge) {
        if (rtc_port->pullup_down_enable) {
            rtc_port_pr_pu(port, 1);
            rtc_port_pr_pd(port, 0);
        }
    } else {
        if (rtc_port->pullup_down_enable) {
            rtc_port_pr_pu(port, 0);
            rtc_port_pr_pd(port, 1);
        }
    }
    rtc_port_pr_wkup_clear_pnd(port);
    rtc_port_pr_wkup_edge(port, rtc_port->edge);
    rtc_port_pr_wkup_en_port(port, 1);
}

static void __hw_power_set_wakeup_IO(u8 index, struct port_wakeup *port)
{
    if (port->iomap == IO_PORT_PR_00 || port->iomap == IO_PORT_PR_01) {
        __this->rtc_port[index].pullup_down_enable = port->pullup_down_enable;
        __this->rtc_port[index].edge            = port->edge;
        __this->rtc_port[index].attribute       = port->attribute;
        __this->rtc_port[index].iomap           = port->iomap;
        __this->rtc_port[index].filter_enable   = port->filter_enable;
    } else {
        __this->port[index].pullup_down_enable = port->pullup_down_enable;
        __this->port[index].edge            = port->edge;
        __this->port[index].attribute       = port->attribute;
        __this->port[index].iomap           = port->iomap;
        __this->port[index].filter_enable   = port->filter_enable;
    }

    u8 group;

    if (port->iomap < IO_MAX_NUM) {
        group = (port->iomap / IO_GROUP_NUM) * IO_GROUP_NUM;
        log_info("Wakeup Index : %d / Group : %d / Edge : %s", index, group, port->edge ? "Falling" : "Rising");
    } else {
        if (port->iomap == IO_PORT_DP) {
            group = GPIOUSB;
        }
        if (port->iomap == IO_PORT_DM) {
            group = GPIOUSB;
        }
        if (port->iomap == IO_LDOIN_DET) {
            group = GPIOP33;
        }
        if (port->iomap == IO_LVCMP_DET) {
            group = GPIOP33;
        }
        if (port->iomap == IO_PORT_PR_00) {
            group = GPIOR;
        }
        if (port->iomap == IO_PORT_PR_01) {
            group = GPIOR;
        }
        log_info("Wakeup Index : %d / Group : %s / Edge : %s", index, "USB", port->edge ? "Falling" : "Rising");
    }

    switch (group) {
        case GPIOA:
        case GPIOB:
        case GPIOD:
        case GPIOC:
            __hw_general_port_wakeup_set(index, port);
            break;
        case GPIOUSB:
            __hw_USB_port_wakeup_set(index, port);
            break;
        case GPIOP33:
            __hw_P33_port_wakeup_set(index, port);
            break;
        case GPIOR:
            __hw_rtc_port_wakeup_set(index, port);
            break;
        default:
            ASSERT(0, "Invalid I/O : %c - %d", group, port->iomap);
            break;
    }
}

static u8 __hw_power_wakeup_handler(void)
{
    struct port_wakeup_driver *port;
    u8 result = 0;
    u8 sfr;
    char s[10];

    sfr = P33_GET_WKUP_PND();

    if (sfr) {
        for (u8 i = 0; i < MAX_WAKEUP_PORT; i++) {
            if (sfr & BIT(i)) {
                P33_SET_WKUP_CPND(i);
                log_info("Port index %d", i);
                result |= __this->port[i].attribute;
                if (port_edge_wakeup_callback) {
                    port_edge_wakeup_callback(i, __this->port[i].iomap);
                }
            }
        }
    }

    return result;
}

static bool __hw_power_wakeup_port_press(void)
{
    return TRUE;
}

void __hw_wakeup_port_init(const struct wakeup_param *param)
{
    const struct port_wakeup *port;

    for (u8 i = 0; i < MAX_WAKEUP_PORT; i++) {
        port = param->port[i];
        if (port && (wkup_en & BIT(i))) {
            __hw_power_set_wakeup_IO(i, port);
        }
    }

    for (u8 j = 0; j < MAX_RTC_PORT; j++) {
        port = param->rtc_port[j];
        if (port) {
            __hw_power_set_wakeup_IO(j, port);
        }
    }

    P33_DUMP_WKUP();
    P33_PORT_DUMP();

    // LEVEL trigger
    P33_LEVEL_WKUP_EN(1);
}

void __hw_wakeup_rtc_init(void)
{
    P33_LEVEL_WKUP_EN(1);
}

void rtc_wakup_source(void);
IRQ_FUNCTION_PROTOTYPE(IRQ_RTC_IDX, wakeup_irq_handler) static void wakeup_irq_handler(void)
{
    u32 event;

    if (P33_WKUP_LEVEL_READ()) {
        __hw_wakeup_source();
        rtc_wakup_source();
    }
    if (P33_2Hz_PENDING_READ()) {
        P33_2Hz_CLEAR_PENDING(1);
        log_info("2Hz");
    }
    if (P33_512Hz_PENDING_READ()) {
        P33_512Hz_CLEAR_PENDING(1);
        log_info("512Hz");
    }
    if (P33_WKUP_PENDING_READ()) {
        P33_WKUPCLEAR_PENDING(1);
    }
}

u8 get_wakeup_source(void)
{
    return wkup_src;
}

u8 get_wakeup_pnd(void)
{
    return wkup_pnd;
}

u8 is_io_wakeup(void)
{
    u8 map = 0;
    // 找出设置成唤醒IO对应第几组唤醒源,可能1个或多个
    if (power_wakeup_param) {
        for (u8 i = 0; i < MAX_WAKEUP_PORT; i++) {
            if (power_wakeup_param->port[i]) {
                map |= BIT(i);
            }
        }
    }
    // IO唤醒,则不去判断是否为LDO5V唤醒
    if (wkup_src & BIT(P3_WKUP_SRC_EDGE)) {
        if (wkup_pnd & map) {
            return wkup_pnd;
        }
    }
    return 0;
}

// 为解决BR23按键短按开机问题(only for br23)
u8 is_ldo5v_wakeup(void)
{
    u8 map = 0;
    // 找出设置成唤醒IO对应第几组唤醒源,可能1个或多个
    if (power_wakeup_param) {
        for (u8 i = 0; i < MAX_WAKEUP_PORT; i++) {
            if (power_wakeup_param->port[i]) {
                map |= BIT(i);
            }
        }
    }
    // IO唤醒,则不去判断是否为LDO5V唤醒
    if (wkup_src & BIT(P3_WKUP_SRC_EDGE)) {
        if (wkup_pnd & map) {
            return 0;
        }
    }
    return (wkup_src & BIT(P3_WKUP_SRC_SUB));
}

void reset_the_wakeup_param(struct wakeup_param *param)
{
    __hw_wakeup_port_init(param);

    __this->sub.attribute = param->sub->attribute;
    __this->charge.attribute = param->charge->attribute;
}

void power_wakeup_init(const struct wakeup_param *param)
{
    power_wakeup_param = param;

    wkup_src = p33_rx_1byte(P3_WKUP_SRC);
    wkup_pnd = p33_rx_1byte(P3_WKUP_PND);

    // Wakeup source - port
    __hw_wakeup_port_init(param);

    __this->sub.attribute = param->sub->attribute;
    __this->charge.attribute = param->charge->attribute;

    p33_tx_1byte(P3_ANA_LAT, 0x00);

    // Wakeup source - RTC
    __hw_wakeup_rtc_init();

    IRQ_REGISTER(IRQ_RTC_WDT_IDX, wakeup_irq_handler, 1);

    log_info("JL_P33->RTC_CON : %x", JL_P33->RTC_CON);
}

void power_wakeup_index_enable(u8 index)
{
    P33_OR_WKUP_CPND(index);
    P33_OR_WKUP_EN(index);
    wkup_en |= BIT(index);
}

void power_wakeup_index_disable(u8 index)
{
    P33_OR_WKUP_CPND(index);
    P33_AND_WKUP_EN(index);
    wkup_en &= ~BIT(index);
}

void power_wakeup_set_wakeup_io(u8 index, struct port_wakeup *port)
{
    __hw_power_set_wakeup_IO(index, port);
}

u8 power_wakeup_exit(void)
{
    return __hw_wakeup_source();
}

const struct wakeup_param *power_get_wakeup_param(void)
{
    return power_wakeup_param;
}

void charge_set_callback(void (*wakup_callback)(void), void (*sub_callback)(void))
{
    charge_wakeup_callback = wakup_callback;
    sub_wakeup_callback = sub_callback;
}

void chargestore_set_callback(void (*callback)(void))
{
    chargestore_ldo5v_fall_callback = callback;
}

void port_edge_wkup_set_callback(void (*wakeup_callback)(u8 index, u32 gpio))
{
    port_edge_wakeup_callback = wakeup_callback;
}

/* ----------------------------------------------------------- */

/**
 * @Unit Test
 */
// error
const struct port_wakeup port0_A0 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_00,
};

const struct port_wakeup port0_A1 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_01,
};

const struct port_wakeup port0_A2 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_02,
};

const struct port_wakeup port0_A3 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_03,
};

const struct port_wakeup port0_A4 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_04,
};

const struct port_wakeup port0_A5 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_05,
};

const struct port_wakeup port0_A6 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_06,
};

const struct port_wakeup port0_A7 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_07,
};

const struct port_wakeup port0_A8 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_08,
};

const struct port_wakeup port0_A9 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_09,
};

const struct port_wakeup port0_A10 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTA_10,
};

const struct port_wakeup port1_B0 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_00,
};

const struct port_wakeup port1_B1 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_01,
};

const struct port_wakeup port1_B2 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_02,
};

const struct port_wakeup port1_B3 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_03,
};

const struct port_wakeup port1_B4 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_04,
};

const struct port_wakeup port1_B5 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_05,
};

const struct port_wakeup port1_B6 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_06,
};

const struct port_wakeup port1_B7 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_07,
};

const struct port_wakeup port1_B8 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTB_08,
};

const struct port_wakeup port3_D0 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTD_00,
};

const struct port_wakeup port3_D1 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTD_01,
};

const struct port_wakeup port3_D2 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTD_02,
};

const struct port_wakeup port3_D3 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTD_03,
};

const struct port_wakeup port3_D4 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTD_04,
};

const struct port_wakeup port3_D5 = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORTD_05,
};

const struct port_wakeup port3_USB_DP = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORT_DP,
};

const struct port_wakeup port3_USB_DM = {
    .edge       = FALLING_EDGE,
    .attribute  = BLUETOOTH_RESUME,
    .iomap      = IO_PORT_DM,
};

static const struct wakeup_param wk_param_test = {

#if 1
    // sample - 4
    .port[0] = &port3_D5,
    .port[1] = &port3_USB_DP,
    .port[2] = &port3_USB_DM,
#endif
};

void power_wakeup_init_test(void)
{
    struct wakeup_param *param = &wk_param_test;
    // Wakeup source - port
    __hw_wakeup_port_init(param);

    IRQ_REGISTER(IRQ_RTC_WDT_IDX, wakeup_irq_handler, 1);

    log_info("JL_P33->RTC_CON : %x", JL_P33->RTC_CON);
}

